Skip to content

Comments

feat(server): implement Resource Scoping for tasks and push notifications#709

Open
sokoliva wants to merge 33 commits intoa2aproject:1.0-devfrom
sokoliva:resource-scoping
Open

feat(server): implement Resource Scoping for tasks and push notifications#709
sokoliva wants to merge 33 commits intoa2aproject:1.0-devfrom
sokoliva:resource-scoping

Conversation

@sokoliva
Copy link
Contributor

@sokoliva sokoliva commented Feb 18, 2026

Description

Introduces caller indentity isolation to ensure clients only access authorized resources, as mandated by the A2A spec.

  • Add ownerfield to TaskMixin and PushNotificationConfig database models.
  • Add last_updated field to TaskMixin for optimized sorting and indexing.
  • Update DatabaseTaskStore, InMemoryTaskStore and DatabasePushNotificationConfigStore to use OwnerResolver.
  • Add Resource Scoping related Unit tests.
  • Add Alembic configuration to enable users to update their own databases with non-optional owner column in tasks and push_notification_configs table and optional last_updated and index (owner, last_updated) in tasks .
  • Distribute alembic configuration, enable CLI commands such as uv run a2a-db for database updating.

Note

  • In src/a2a/server/tasks/database_task_store.py list method, Gemini suggested a refactor of pagination. I thoroughly reviewed it and comfirmed that the logic is the same and that readability of code improved so I decided to accept it.
  • It seems there was a functional bug in InMemoryPushNotificationConfigStore delete_info method. When config_id is None and only task_id was provided it would search for configs mapped to task_id with config.id=task_id, contrary to delete_info method of DatabasePushNotificationConfigStore where if config_id is None, all configurations for the task are deleted. Unfortunately, I did not find intended behavior defined in the spec, but behavior of DatabasePushNotificationConfigStore's delete_info seems more logical.

Breaking changes

  • added non-optional owner field to the Task Model. Use alembic configuration to update your database.

  • Ensure the tests and linter pass (Run bash scripts/format.sh from the repository root to format)

  • Appropriate docs were updated (if necessary)

Fixes #610 🦕

…fications`

Introduces caller indentity isolation to ensure clients only access authorized resources, as mandated by the A2A spec.

- Add 'owner' field to `TaskMixin` and `PushNotificationConfig` database models.
 - Add 'last_updated' field to `TaskMixin` for optimized sorting and indexing.
- Update `DatabaseTaskStore`, `InMemoryTaskStore` and `DatabasePushNotificationConfigStore` to use `OwnerResolver`.
- Add relevant Unit tests.
- Add Alembic configuration to enable users to update their own databases with non-optional `owner` field in `tasks` table.
@sokoliva sokoliva requested review from a team and a2a-bot as code owners February 18, 2026 16:19
@sokoliva sokoliva changed the base branch from main to 1.0-dev February 18, 2026 16:22
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @sokoliva, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the server's data management capabilities by implementing robust resource scoping for tasks and push notification configurations, ensuring that users can only access their own data. It also introduces a flexible 'ListTasks' API, enabling efficient retrieval and management of tasks with advanced filtering and pagination. The integration of Alembic provides a structured approach to future database schema evolution.

Highlights

  • Resource Scoping Implementation: Introduced an 'owner' field to 'TaskMixin' and 'PushNotificationConfig' database models, along with an 'OwnerResolver' to enforce caller identity isolation for tasks and push notification configurations.
  • Task Listing API: Added a new 'ListTasks' API endpoint across gRPC, JSON-RPC, and REST transports, supporting filtering, pagination, and sorting by 'last_updated' timestamp.
  • Database Migration Setup: Integrated Alembic for managing database schema migrations, including an initial migration to add the 'owner' column to the 'tasks' table.
  • Performance Enhancements: Added a 'last_updated' field to 'TaskMixin' and an index on '(owner, last_updated)' to optimize task retrieval and sorting.
Changelog
  • alembic.ini
    • Configured Alembic for database migrations.
  • alembic/README
    • Documented Alembic migration commands and troubleshooting.
  • alembic/env.py
    • Configured Alembic environment for asynchronous SQLAlchemy operations.
  • alembic/script.py.mako
    • Provided a Mako template for generating new Alembic migration scripts.
  • alembic/versions/6419d2d130f6_add_owner_to_task.py
    • Added an initial migration script to introduce a non-nullable 'owner' column to the 'tasks' table with a default value.
  • pyproject.toml
    • Added Alembic tool configuration.
  • src/a2a/client/base_client.py
    • Updated imports and added an abstract 'list_tasks' method to the base client.
  • src/a2a/client/client.py
    • Updated imports and added an abstract 'list_tasks' method to the client interface.
  • src/a2a/client/transports/base.py
    • Updated imports and added an abstract 'list_tasks' method to the base transport.
  • src/a2a/client/transports/grpc.py
    • Implemented the 'list_tasks' method for the gRPC transport, including proto conversions and default page size.
  • src/a2a/client/transports/jsonrpc.py
    • Implemented the 'list_tasks' method for the JSON-RPC transport, handling request/response models and error cases.
  • src/a2a/client/transports/rest.py
    • Implemented the 'list_tasks' method for the REST transport, including query parameter conversion and handling.
  • src/a2a/grpc/a2a_pb2.py
    • Generated new protobuf definitions to include 'ListTasksRequest' and 'ListTasksResponse' messages and the 'ListTasks' service method.
  • src/a2a/grpc/a2a_pb2.pyi
    • Updated the protobuf type hints to include 'ListTasksRequest' and 'ListTasksResponse'.
  • src/a2a/grpc/a2a_pb2_grpc.py
    • Generated new gRPC service code to include the 'ListTasks' method.
  • src/a2a/server/apps/jsonrpc/jsonrpc_app.py
    • Updated to recognize and process 'ListTasksRequest' for JSON-RPC.
  • src/a2a/server/models.py
    • Added 'datetime' import, 'Index' import, 'owner' and 'last_updated' fields to 'TaskMixin', and 'owner' field to 'PushNotificationConfigMixin'.
    • Defined a table argument for a combined index on 'owner' and 'last_updated'.
  • src/a2a/server/owner_resolver.py
    • Added a new module defining 'OwnerResolver' type and a default 'resolve_user_scope' function.
  • src/a2a/server/request_handlers/default_request_handler.py
    • Implemented the 'on_list_tasks' method, handling filtering, pagination, artifact exclusion, and history length application.
  • src/a2a/server/request_handlers/grpc_handler.py
    • Implemented the gRPC handler for the new 'ListTasks' method.
  • src/a2a/server/request_handlers/jsonrpc_handler.py
    • Implemented the JSON-RPC handler for the new 'tasks/list' method.
  • src/a2a/server/request_handlers/request_handler.py
    • Added an abstract 'on_list_tasks' method to the base request handler interface.
  • src/a2a/server/request_handlers/response_helpers.py
    • Updated type definitions to support 'ListTasksResult' in JSON-RPC responses.
  • src/a2a/server/request_handlers/rest_handler.py
    • Implemented the REST handler for 'list_tasks' and adjusted 'list_push_notifications' to be unimplemented.
  • src/a2a/server/tasks/database_push_notification_config_store.py
    • Modified methods ('init', '_to_orm', 'set_info', 'get_info', 'delete_info') to incorporate owner-based scoping for push notification configurations.
  • src/a2a/server/tasks/database_task_store.py
    • Modified methods ('init', '_to_orm', 'save', 'get', 'delete') to incorporate owner-based scoping and 'last_updated' field.
    • Implemented the 'list' method with filtering, sorting, and pagination.
  • src/a2a/server/tasks/inmemory_task_store.py
    • Modified methods ('init', 'save', 'get', 'delete') to incorporate owner-based scoping.
    • Implemented the 'list' method with filtering, sorting, and pagination.
  • src/a2a/server/tasks/push_notification_config_store.py
    • Updated abstract methods to include 'ServerCallContext' for owner resolution.
  • src/a2a/server/tasks/task_store.py
    • Introduced 'TasksPage' model and an abstract 'list' method for task pagination.
  • src/a2a/types.py
    • Added new Pydantic models for 'ListTasksParams', 'ListTasksRequest', 'ListTasksResult', and 'ListTasksSuccessResponse', and updated relevant type unions.
  • src/a2a/utils/constants.py
    • Added 'DEFAULT_LIST_TASKS_PAGE_SIZE' constant.
  • src/a2a/utils/proto_utils.py
    • Added utility functions for converting between 'ListTasksParams'/'ListTasksResult' and their protobuf equivalents.
  • src/a2a/utils/task.py
    • Added utility functions 'encode_page_token' and 'decode_page_token' for base64 encoding/decoding of pagination tokens.
  • tests/client/test_client_factory.py
    • Updated client transport extension assertions.
  • tests/client/transports/test_grpc_client.py
    • Added test cases for the new 'list_tasks' gRPC client method.
  • tests/client/transports/test_jsonrpc_client.py
    • Added test cases for the new 'list_tasks' JSON-RPC client method.
  • tests/extensions/test_common.py
    • Added 'pytest' import.
  • tests/integration/test_client_server_integration.py
    • Added integration tests for 'list_tasks' across HTTP (JSON-RPC, REST) and gRPC transports.
  • tests/server/request_handlers/test_default_request_handler.py
    • Added unit tests for the 'on_list_tasks' method, covering success, artifact exclusion, and history length.
    • Updated 'on_message_send' calls to pass context to 'task_store.get'.
  • tests/server/request_handlers/test_grpc_handler.py
    • Added unit tests for the 'ListTasks' gRPC handler.
  • tests/server/request_handlers/test_jsonrpc_handler.py
    • Added unit tests for the 'list_tasks' JSON-RPC handler.
  • tests/server/tasks/test_database_push_notification_config_store.py
    • Added 'TestUser' class and tests for owner-based resource scoping in the database push notification config store.
  • tests/server/tasks/test_database_task_store.py
    • Added 'TestUser' class and extensive tests for 'list_tasks' with various filters, pagination, and owner-based scoping in the database task store.
  • tests/server/tasks/test_inmemory_task_store.py
    • Added 'TestUser' class and extensive tests for 'list_tasks' with various filters, pagination, and owner-based scoping in the in-memory task store.
  • tests/server/test_owner_resolver.py
    • Added a new test file with unit tests for the 'resolve_user_scope' function.
  • tests/utils/test_proto_utils.py
    • Added unit tests for the new 'list_tasks_request' and 'list_tasks_result' proto utility conversions.
  • tests/utils/test_task.py
    • Added unit tests for the new 'encode_page_token' and 'decode_page_token' utility functions.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/linter.yaml
    • .github/workflows/unit-tests.yml
  • Ignored by pattern: CHANGELOG.md (1)
    • CHANGELOG.md
Activity
  • The author, sokoliva, initiated this pull request to implement caller identity isolation for tasks and push notifications, addressing issue [Feat]: Resource Scoping #610.
  • The PR also aims to optimize task retrieval by adding a 'last_updated' field and indexing.
  • A checklist is included in the PR description, indicating the author's intention to ensure tests and linter pass, and that appropriate documentation is updated.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces resource scoping by adding an owner field to tasks and push notification configurations, which is a great step towards multi-tenancy and security. The implementation across the database and in-memory stores is consistent. I've also noticed the addition of Alembic for database migrations, which is excellent for managing schema changes. My feedback focuses on improving the Alembic configuration, strengthening the data model for timestamps, and making the owner resolution logic more robust.

@sokoliva sokoliva requested a review from ishymko February 19, 2026 10:48
sokoliva and others added 7 commits February 19, 2026 10:55
- remove redundant 'index=True' in owner field declaration
- add owner resource scoping to `InMemoryPushNotificationConfigStore` and a related unit test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: README.md?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use migration(s) for all schema updates made in this PR: last_updated and indexes as well.

Currently we have a flag to create tables via create_all which is not going to create new columns inside existing tables. * - maybe it will create new index, but it requires checking.

asyncio.run(run_async_migrations())


if context.is_offline_mode():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use it via CLI? Can't find it in the cli.py or in the docs. It could be beneficial for environments where DB updates are done only via scripts with manual DBA reviews to generate SQL instead of running it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add --verbose -v flag for debug logs, not sure about exact log levels in sqlalchemy but at least printing executed SQL statements would be good.


```bash
# Bring the database to the latest version
a2a-db
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running this command in a fresh environment will fail with alembic package missing error. alembic is a dev dependency currently so for package users it won't be installed.

You can reproduce it by creating a "fake" consumer project and installing whl there:

uv build

which is going to output something like dist/a2a_python-*.whl.

And in the new folder

uv venv
uv pip install <whl path from uv build>
uv run a2a-db

I believe we have two options:

  1. Instruct users to manually install alembic as their dev dependency if they want to use this CLI.
  2. Create a separate "extra" which will be used for this.

self.owner_resolver = owner_resolver

def _get_owner_tasks(self, owner: str) -> dict[str, Task]:
return self.tasks.get(owner, {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that fallback to {} is required after changing tasks to defaultdict(dict).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's consider if some check can be added to fail fast on non-applied migrations. Currently it's going to fail during request execution, maybe it can be checked in initialize method to fail fast?

We can follow-up with this in a separate PR/issue as it's rather a safety net, but given that we're establishing DB migrations infrastructure it can be beneficial to research this aspect as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat]: Resource Scoping

3 participants